package pl.tom.social.fetcher.crawler;

import com.torunski.crawler.events.IParserEventListener;
import com.torunski.crawler.events.ParserEvent;
import com.torunski.crawler.util.LinksUtil;
import java.util.Collection;
import java.util.Date;
import org.apache.log4j.Level;
import org.apache.log4j.Logger;
import pl.tom.social.common.types.PageEntryType;
import pl.tom.social.common.sitetool.ISiteTools;
import pl.tom.social.common.types.PageType;
import pl.tom.social.common.utils.URLTools;
import pl.tom.social.dal.SocialDatabaseFacade;
import pl.tom.social.dal.entity.Site;
import pl.tom.social.dal.entity.SiteEntry;
import pl.tom.social.dal.entity.SiteEntryComment;
import pl.tom.social.dal.entity.SiteLink;
import pl.tom.social.common.sitetool.SiteToolsFactory;
import pl.tom.social.dal.entity.SiteSiteLink;

public class SiteParserEventListener implements IParserEventListener {

	private static final Logger logger = Logger.getLogger(SiteParserEventListener.class);
	ISiteTools tools;
	Site site;

	public SiteParserEventListener(Site site) {
		this.site = site;
		this.tools = SiteToolsFactory.getTools(site);
	}

	@Override
	public void parse(ParserEvent event) {
		SocialDatabaseFacade db = new SocialDatabaseFacade();
		String url = event.getLink().getURI();
		PageType pageType = tools.getPageType(url, site.getUrl());

		if(pageType != PageType.Other && !this.pageExists(db, url, pageType)) {
			Object obj = event.getPageData().getData();
			String page = (String) obj;
			this.processPage(db, url, page, pageType, event);
		}
	}

	private boolean pageExists(SocialDatabaseFacade db, String url, PageType pageType) {
		try {
			switch(pageType) {
				case Home:
					if(db.getSiteEntriesNr(site) > 0) {
						return true;
					}
					break;
				case Entry:
					if(db.getSiteEntry(url) != null) {
						return true;
					}
					break;
				case Comment:
					if(db.getSiteEntryComment(url) != null) {
						return true;
					}
					break;
			}
		} catch(Exception ex) {
			logger.log(Level.ERROR, null, ex);
		}
		return false;
	}

	private void processPage(SocialDatabaseFacade db, String url, String page, PageType pageType, ParserEvent event) {
		Date date = new Date();
		String body = tools.getBlogEntryBody(page, pageType);

		switch(pageType) {
			case Entry:
				PageEntryType entryType = tools.getEntryType(url);
				String title = tools.getBlogEntryTitle(url, page);
				date = tools.getBlogEntryDate(page);
				SiteEntry entry = new SiteEntry(url, title, entryType.ordinal(), date, body, site);
				db.persistEntity(entry);
				break;
			case Comment:
				SiteEntry parent = tools.getEntryFromComment(page);
				if(parent != null) {
					String commentAuthor = tools.getCommentAuthor(body);
					if(commentAuthor != null && !commentAuthor.isEmpty()) {
						Date commentDate = tools.getCommentDate(body);
						SiteEntryComment comment = new SiteEntryComment(url, commentAuthor, commentDate, null, body,
							parent);
						db.persistEntity(comment);
					} else {
						logger.log(Level.WARN, "Author not found for " + url);
					}
				} else {
					logger.log(Level.WARN, "Parent entry not found for " + url);
				}
				break;
		}
		@SuppressWarnings("unchecked")
		Collection<String> links = LinksUtil.DEFAULT_LINK_EXTRACTOR.retrieveLinks(url, body, null);
		links.removeAll(event.getOutLinks());
		this.processLinks(db, links, date);
	}

	private void processLinks(SocialDatabaseFacade db, Collection<String> links, Date date) {
		for(String link : links) {
			try {
				String cleanLink = URLTools.clean(link);
				if(cleanLink.compareTo(site.getUrl()) == 0)
					continue;
				SiteLink siteLink = db.getSiteLink(cleanLink);
				if(siteLink == null) {
					siteLink = new SiteLink(cleanLink);
					db.persistEntity(siteLink);
				}
				SiteSiteLink connector = db.getSiteLinkSitePK(site, siteLink);
				if(connector == null) {
					connector = new SiteSiteLink(site.getId(), siteLink.getId(), date, 1);
					db.persistEntity(connector);
				} else if(!date.equals(connector.getAdded())) {
					if(date.before(connector.getAdded())) {
						connector.setAdded(date);
					}
					connector.setCount(connector.getCount() + 1);
					db.mergeEntity(connector);
				}
			} catch(IllegalArgumentException ex) {
				logger.log(Level.WARN, "Url cleaner could not proccess link: " + link, ex);
			} catch(Exception ex) {
				logger.log(Level.ERROR, null, ex);
			}
		}
	}

	public void parseLinks(SocialDatabaseFacade db, String url, String body, Date date) {
		Collection<String> links = LinksUtil.DEFAULT_LINK_EXTRACTOR.retrieveLinks(url, body, null);
		this.processLinks(db, links, date);
	}
}
